﻿/*  ----------------------------------------------------------------------------
 *  SysServer
 *  ----------------------------------------------------------------------------
 *  File:       Win32IOCPExtern.cs
 *  Creater:     SEEN\MistSeen
 *  ----------------------------------------------------------------------------
 *  $Date: 2010-09-06 10:36:01 +0000 (Mon, 06 Sep 2010) $
 *  $Rev: 15 $
 *  $Author: ScorDeity $
 *  $URL: http://sysserver.googlecode.com/svn/trunk/src/SysServer/Win32IOCPExtern.cs $
 *  $Id: Win32IOCPExtern.cs 15 2010-09-06 10:36:01Z ScorDeity $
 */

using System;
using System.Runtime.InteropServices;
using SysServer.Threading;
using Microsoft.Win32.SafeHandles;

namespace SysServer
{
    public static class IOCPEx
    {
        /*
         * 先用函数CreateIoCompletionPort来创建一个消息队列，
         * 然后使用GetQueuedCompletionStatus函数来从队列获取消息，
         * 使用函数PostQueuedCompletionStatus来向队列里发送消息。通过这三个函数就实现完成端口的消息循环处理。
         *          
         * 调用GetQueuedCompletionStatus函数，某个线程就会等待一个完成包进入到完成端口的队列中，而不是直接等待异步I/O请求完成。线程（们）就会阻塞于它们的运行在完成端口（按照后进先出队列顺序的被释放）。这就意味着当一个完成包进入到完成端口的队列中时，系统会释放最近被阻塞在该完成端口的线程。
         * 
         * 一、工作者线程与完成端口
                成功创建一个完成端口后，便可开始将套接字句柄与对象关联到一起。但在关联套接字之前，首先必须创建一个或多个“工作者线程”，
         *     以便在I/O请求投递给完成端口对象后，为完成端口提供服务。在这个时候，到底应创建多少个线程，以便为完成端口提供服务呢？
         *     这实际正是完成端口模型显得颇为“复杂”的一个方面，因为服务I/O请求所需的数量取决于应用程序的总体设计情况。
         *     在此要记住的一个重点在于，在我们调用CreateIoCompletionPort时指定的并发线程数量，与打算创建的工作者线程数量相比，它们代表的并非同一件事情。
         *     早些时候，我们曾建议大家用CreateIoCompletionPort函数为每个处理器都指定一个线程（处理器的数量有多少，便指定多少线程）以避免由于频繁的线程“场景”交换活动，
         *     从而影响系统的整体性能,这个对一般的情况来说是对的.但是需要具体问题具体分析.首先得明确参数的意义:
         *     CreateIoCompletionPort函数的NumberOfConcurrentThreads参数明确指示系统：
         *          在一个完成端口上，一次只允许n个工作者线程运行,在完成端口上创建的工作者线程数量超出n个，那么在同一时刻，最多只允许n个线程运行。
         *          但实际上，在一段较短的时间内，系统有可能超过这个值，但很快便会把它减少至事先在CreateIoCompletionPort函数中设定的值。
         *     可以得到,NumberOfConcurrentThreads的设定应该涉及到了"工作线程"的运行状态.
         *     假如
                   A. 我们的某个工作者线程调用了一个函数，比如Sleep或WaitForSingleObject，但却进入了暂停（锁定或挂起）状态，那么允许另一个线程代替它的位置。
         *             换言之，我们希望随时都能执行尽可能多的线程(当然，最大的线程数量是事先在CreateIoCompletionPort调用里设定好的);
         *             这样一来，我们事先预计到自己的线程有可能暂时处于停顿状态，那么最好能够创建比CreateIoCompletionPort的NumberOfConcurrentThreads参数的值多的线程，以便到时候充分发挥系统的潜力;
         *         B.我们的工作线程是作为长时间的运行,或者说,他基本不会关闭,那么,我们最好的将自己的工作线程 设定和CreateIoCompletionPort的NumberOfConcurrentThreads参数一致,并且等于Cpu的个数.
         */

        #region ========================= Constants =========================
        /// <summary> SimTypeConst: This represents the Win32 Invalid Handle Value Macro </summary>
        public const UInt32 INVALID_HANDLE_VALUE = 0xffffffff;

        /// <summary> SimTypeConst: This represents the Win32 INFINITE Macro </summary>
        public const UInt32 INIFINITE = 0xffffffff;

        /// <summary> SimTypeConst: This tells the IOCP Function to shutdown </summary>
        public const Int32 SHUTDOWN_IOCPTHREAD = 0x7fffffff;
        #endregion

        #region ================== Win32 Function Prototypes ==================
        /// <summary>
        /// <remarks>
        /// 1. 用于创建一个完成端口对象。
        /// 2. 将一个句柄同完成端口关联到一起。
        /// </remarks> 
        /// </summary>
        /// <param name="FileHandle">指定一个要同完成端口关联在一起的套接字句柄</param>
        /// <param name="ExistingCompletionPort">指定的是一个现有的完成端口</param>
        /// <param name="CompletionKey">（完成键）参数则指定要与某个特定套接字句柄关联在一起的“单句柄数据”；
        /// 之所以把它叫作“单句柄数据”，是由于它只对应着与那个套接字句柄关联在一起的数据。可将其作为指向一个数据结构的指针，来保存套接字句柄；在那个结构中，同时包含了套接字的句柄，以及与那个套接字有关的其他信息。</param>
        /// <param name="uiNumberOfConcurrentThreads">
        /// 并发线程的数量.定义了在一个完成端口上，同时允许执行的线程数量。
        /// 理想情况下，每个处理器各自负责一个线程的运行，为完成端口提供服务，避免过于频繁的线程“场景”切换
        /// 。若将该参数设为0，表明系统内安装了多少个处理器，便允许同时运行多少个线程,但是要根据具体的应用(线程是否一直运行,是否存在频繁的挂起?)</param>
        /// <returns>为完成端口分配了一个套接字句柄后，用来对那个端口进行标定（引用）</returns>
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        public unsafe static extern UInt32 CreateIoCompletionPort(UInt32 FileHandle, UInt32 ExistingCompletionPort, UInt32* CompletionKey, UInt32 uiNumberOfConcurrentThreads);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="FileHandle"></param>
        /// <param name="ExistingCompletionPort"></param>
        /// <param name="CompletionKey"></param>
        /// <param name="NumberOfConcurrentThreads"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);
        /// <summary>
        /// 只是关闭了线程对象的句柄,并没有关闭线程的运行.
        /// </summary>
        /// <param name="hObject"></param>
        /// <returns></returns>
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        public unsafe static extern Boolean CloseHandle(UInt32 hObject);

        /// <summary>
        /// 一旦所有套接字句柄都已关闭，便需在完成端口上，终止所有工作者线程的运行。要想做到这一点， 需要使用PostQueuedCompletionStatus函数，向每个工作者线程都发送一个特殊的完成数据包。该函数会指示每个线程都“立即结束并退出”。
        /// </summary>
        /// <param name="hCompletionPort">CompletionPort参数指定想向其发送一个完成数据包的完成端口对象。</param>
        /// <param name="uiSizeOfArgument"></param>
        /// <param name="puiUserArg"></param>
        /// <param name="pOverlapped"></param>
        /// <returns></returns>
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        public unsafe static extern Boolean PostQueuedCompletionStatus(UInt32 hCompletionPort, UInt32 uiSizeOfArgument, UInt32* puiUserArg, OVERLAPPED* pOverlapped);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="CompletionPort"></param>
        /// <param name="dwNumberOfBytesTransferred"></param>
        /// <param name="dwCompletionKey"></param>
        /// <param name="lpOverlapped"></param>
        /// <returns></returns>
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        public static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
        /// <summary>
        /// 获取排队完成状态
        /// </summary>
        /// <param name="CompletionPort">等待的完成端口</param>
        /// <param name="pSizeOfArgument">负责在完成了一次I/O操作后（如WSASend或WSARecv），接收实际传输的字节数</param>
        /// <param name="puiUserArg">递进入CreateIoCompletionPort函数的套接字返回“单句柄数据”</param>
        /// <param name="ppOverlapped">用于接收完成的I/O操作的重叠结果,可用它获取每个I/O操作的数据</param>
        /// <param name="uiMilliseconds">指定调用者希望等待一个完成数据包在完成端口上出现的时间。假如将其设为INFINITE(0xffffffff)，调用会无休止地等待下去</param>
        /// <returns></returns>
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        public unsafe static extern Boolean GetQueuedCompletionStatus(UInt32 CompletionPort, UInt32* pSizeOfArgument, UInt32* puiUserArg, OVERLAPPED** ppOverlapped, UInt32 uiMilliseconds);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="CompletionPort"></param>
        /// <param name="lpNumberOfBytesTransferred"></param>
        /// <param name="lpCompletionKey"></param>
        /// <param name="lpOverlapped"></param>
        /// <param name="dwMilliseconds"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort, out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, uint dwMilliseconds);

        #endregion
    }
}